package com.cfpamf.ms.insur.pay.integration.alipay;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayResponse;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.cfpamf.common.ms.exception.MSException;
import com.cfpamf.ms.insur.pay.constant.BizExceptEnum;
import com.cfpamf.ms.insur.pay.facade.constant.PayMethodEnum;
import com.cfpamf.ms.insur.pay.facade.constant.PayStatusEnum;
import com.cfpamf.ms.insur.pay.integration.BankPayService;
import com.cfpamf.ms.insur.pay.integration.dto.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

import static com.cfpamf.ms.insur.pay.pojo.convert.PayOrderConvert.CONVERT;


/**
 * 支付宝接口
 * 电脑网页支付/手机网页支付/当面付二维码支付/APP支付
 * 交易查询接口
 * 查询对账单下载地址接口
 *
 * @author zhangnayi
 */
@Slf4j
@Service("ALIPAY")
public class AliPayService implements BankPayService {

    /**
     * 支付宝client工厂
     */
    @Autowired
    private AliPayClientFactory clientFactory;

    @Override
    public BankPreCreateOrderRetDTO bankPreCreateOrder(BankPreCreateOrderParamDTO params) {
        String body = null;
        try {
            if (Objects.equals(params.getPayMethod(), PayMethodEnum.APP.getTypeCode())) {
                body = appPay(params);
            } else if (Objects.equals(params.getPayMethod(), PayMethodEnum.QRCODE.getTypeCode())) {
                body = qrcodePay(params);
            } else if (Objects.equals(params.getPayMethod(), PayMethodEnum.H5.getTypeCode())) {
                body = mobilePay(params);
            } else if (Objects.equals(params.getPayMethod(), PayMethodEnum.PC.getTypeCode())) {
                body = pcPay(params);
            } else {
                throw new MSException(BizExceptEnum.PAY_METHOD_ERROR);
            }
        } catch (AlipayApiException e) {
            throw new MSException(BizExceptEnum.ALIPAY_API_ERROR);
        }
        BankPreCreateOrderRetDTO retDTO = new BankPreCreateOrderRetDTO();
        retDTO.setBody(body);
        return retDTO;
    }

    @Override
    public BankPayOrderRetDTO bankPayOrder(BankPayOrderParamDTO params) {
        return null;
    }


    @Override
    public BankQueryOrderRetDTO bankQueryOrderInfo(BankQueryOrderParamDTO params) {
        try {
            AlipayTradeQueryResponse tradeQueryResp = queryAliPayTrade(params.getSourceSys(), params.getPayTrxNo());
            BankQueryOrderRetDTO retDTO = new BankQueryOrderRetDTO();
            if (Objects.equals(tradeQueryResp.getTradeStatus(), "TRADE_SUCCESS")) {
                retDTO = CONVERT.alipayTradeQueryResponse2QueryOrderRetDTO(tradeQueryResp);
                retDTO.setPayStatus(PayStatusEnum.PAY_SUCCESS.getStatusCode());
            } else if (Objects.equals(tradeQueryResp.getTradeStatus(), "WAIT_BUYER_PAY")) {
                retDTO.setPayTrxNo(params.getPayTrxNo());
                retDTO.setPayStatus(PayStatusEnum.TO_PAID.getStatusCode());
            } else {
                retDTO.setPayTrxNo(params.getPayTrxNo());
                retDTO.setPayStatus(PayStatusEnum.TO_PAID.getStatusCode());
            }
            return retDTO;
        } catch (AlipayApiException e) {
            log.info("支付宝查询交易结果接口调用失败", e);
            throw buildAliPayApiException(e);
        }
    }

    @Override
    public BankRefundOrderRetDTO bankRefundOrder(BankRefundOrderParamDTO params) {
        try {
            String body = tradeRefund(params);
            BankRefundOrderRetDTO retDTO = new BankRefundOrderRetDTO();
            retDTO.setBody(body);
            return retDTO;
        } catch (AlipayApiException e) {
            log.error("支付宝退款接口调用失败", e);
            throw buildAliPayApiException(e);
        }
    }

    @Override
    public BankQueryRefundRetDTO bankQueryRefund(BankQueryRefundParamDTO params) {
        try {
            AlipayTradeFastpayRefundQueryResponse resp = queryAliPayRefund(params.getSourceSys(), params.getPayTrxNo(), params.getRefundTrxNo());
            BankQueryRefundRetDTO retDTO = new BankQueryRefundRetDTO();
            if (resp.getRefundStatus() == null || Objects.equals(resp.getRefundStatus(), "REFUND_SUCCESS")) {
                retDTO.setPayTrxNo(params.getPayTrxNo());
                retDTO.setBankPayTrxNo(resp.getOutTradeNo());
                retDTO.setRefundTrxNo(resp.getOutRequestNo());
                retDTO.setOrderAmount(new BigDecimal(resp.getTotalAmount()));
                retDTO.setRefundAmount(new BigDecimal(resp.getRefundAmount()));
                retDTO.setRefundStatus(PayStatusEnum.REFUND_SUCCESS.getStatusCode());
                retDTO.setRefundTrxNo(resp.getOutRequestNo());
                retDTO.setRefundTime(resp.getGmtRefundPay());
                return retDTO;
            }
            retDTO.setRefundStatus(PayStatusEnum.REFUNDING.getStatusCode());
            return retDTO;
        } catch (AlipayApiException e) {
            log.error("支付宝退款接口调用失败", e);
            throw buildAliPayApiException(e);
        }
    }

    @Override
    public BankPayCheckNotifyRetDTO bankCheckPayNotify(String sourceSys, HttpServletRequest request) {
        BankPayCheckNotifyRetDTO retDTO = new BankPayCheckNotifyRetDTO();
        Map<String, String> params = AliPayUtil.toMap(request);
        try {
            boolean verifyResult = AlipaySignature.rsaCheckV1(params, clientFactory.getSystemAliPayProperty(sourceSys).getAlipayPublicKey()
                    , AliPayConstants.CHARSET_UTF8, clientFactory.getSystemAliPayProperty(sourceSys).getSignType());
            log.info("验签结果：{}",verifyResult);
            if (verifyResult) {
                retDTO.setPaySuccess(true);
                retDTO.setRequestBody(JSON.toJSONString(params));
                retDTO.setRespBody(AliPayConstants.ALIPAY_NOTIFY_RESPONSE_SUCCESS);
                retDTO.setPayTrxNo(params.get("out_trade_no"));
                return retDTO;
            }
        } catch (AlipayApiException e) {
            log.error("支付宝回调解析失败", e);
        }
        retDTO.setPaySuccess(false);
        retDTO.setRequestBody(JSON.toJSONString(params));
        retDTO.setRespBody(AliPayConstants.ALIPAY_NOTIFY_RESPONSE_FAIL);
        return retDTO;
    }

    @Override
    public BankRefundCheckNotifyRetDTO bankCheckRefundNotify(String sourceSys, HttpServletRequest request) {
        return null;
    }

    /**
     * 电脑网页支付
     *
     * @param dto
     * @return
     * @throws AlipayApiException
     */
    private String pcPay(BankPreCreateOrderParamDTO dto) throws AlipayApiException {
        AlipayTradePagePayModel pagePayModel = new AlipayTradePagePayModel();
        pagePayModel.setOutTradeNo(dto.getPayTrxNo());
        pagePayModel.setSubject(dto.getProductName());
        pagePayModel.setTotalAmount(dto.getOrderAmount().toString());
        pagePayModel.setBody(dto.getProductName());
        pagePayModel.setProductCode(AliPayConstants.ALIPAY_PAGE_PAY_PRODUCT_CODE);
        AlipayTradePagePayRequest pagePayRequest = new AlipayTradePagePayRequest();
        pagePayRequest.setReturnUrl(dto.getReturnUrl());
        pagePayRequest.setNotifyUrl(clientFactory.getSystemAliPayProperty(dto.getSourceSys()).getPayNotifyUrl());
        pagePayRequest.setBizModel(pagePayModel);
        log.info("支付宝电脑网页支付 request={}", JSON.toJSONString(pagePayRequest));
        AlipayTradePagePayResponse pagePayResponse = clientFactory.getAlipayClient(dto.getSourceSys()).pageExecute(pagePayRequest);
        log.info("支付宝电脑网页支付 response={}", JSON.toJSONString(pagePayResponse));
        if (pagePayResponse.isSuccess()) {
            return pagePayResponse.getBody();
        }
        throw buildAliPayBizException(pagePayResponse);
    }

    /**
     * 手机网页支付
     *
     * @param dto
     * @return
     * @throws AlipayApiException
     */
    private String mobilePay(BankPreCreateOrderParamDTO dto) throws AlipayApiException {
        AlipayTradeWapPayModel wapPayModel = new AlipayTradeWapPayModel();
        wapPayModel.setOutTradeNo(dto.getPayTrxNo());
        wapPayModel.setSubject(dto.getProductName());
        wapPayModel.setTotalAmount(dto.getOrderAmount().toString());
        wapPayModel.setBody(dto.getProductName());
        wapPayModel.setTimeoutExpress(dto.getOrderPeriod() + "m");
        wapPayModel.setProductCode(AliPayConstants.ALIPAY_WAP_PAY_PRODUCT_CODE);
        AlipayTradeWapPayRequest wapPayRequest = new AlipayTradeWapPayRequest();
        wapPayRequest.setBizModel(wapPayModel);
        wapPayRequest.setNotifyUrl(clientFactory.getSystemAliPayProperty(dto.getSourceSys()).getPayNotifyUrl());
        wapPayRequest.setReturnUrl(dto.getReturnUrl());
        log.info("支付宝手机网页支付 request={}", JSON.toJSONString(wapPayRequest));
        AlipayTradeWapPayResponse wapPayResponse = clientFactory.getAlipayClient(dto.getSourceSys()).pageExecute(wapPayRequest);
        log.info("支付宝手机网页支付 response={}", JSON.toJSONString(wapPayResponse));
        if (wapPayResponse.isSuccess()) {
            return wapPayResponse.getBody();
        }
        throw buildAliPayBizException(wapPayResponse);
    }

    /**
     * 当面付二维码支付
     *
     * @param dto
     * @return
     * @throws AlipayApiException
     */
    private String qrcodePay(BankPreCreateOrderParamDTO dto) throws AlipayApiException {
        AlipayTradePrecreateModel precreateModel = new AlipayTradePrecreateModel();
        precreateModel.setOutTradeNo(dto.getPayTrxNo());
        precreateModel.setTotalAmount(dto.getOrderAmount().toString());
        precreateModel.setSubject(dto.getProductName());
        precreateModel.setStoreId(clientFactory.getSystemAliPayProperty(dto.getSourceSys()).getMerchantId());
        precreateModel.setTimeoutExpress(dto.getOrderPeriod() + "m");
        AlipayTradePrecreateRequest precreateRequest = new AlipayTradePrecreateRequest();
        precreateRequest.setBizModel(precreateModel);
        precreateRequest.setNotifyUrl(clientFactory.getSystemAliPayProperty(dto.getSourceSys()).getPayNotifyUrl());
        log.info("支付宝二维码支付 request={}", JSON.toJSONString(precreateRequest));
        AlipayTradePrecreateResponse precreateResponse = clientFactory.getAlipayClient(dto.getSourceSys()).execute(precreateRequest);
        log.info("支付宝二维码支付 response={}", JSON.toJSONString(precreateResponse));
        if (precreateResponse.isSuccess()) {
            return precreateResponse.getQrCode();
        }
        throw buildAliPayBizException(precreateResponse);
    }

    /**
     * 手机app支付
     *
     * @param dto
     * @return
     * @throws AlipayApiException
     */
    private String appPay(BankPreCreateOrderParamDTO dto) throws AlipayApiException {
        AlipayTradeAppPayModel appPayModel = new AlipayTradeAppPayModel();
        appPayModel.setOutTradeNo(dto.getPayTrxNo());
        appPayModel.setTotalAmount(dto.getOrderAmount().toString());
        appPayModel.setBody(dto.getProductName());
        appPayModel.setSubject(dto.getProductName());
        appPayModel.setProductCode(AliPayConstants.ALIPAY_APP_PAY_PRODUCT_CODE);
        appPayModel.setTimeoutExpress(dto.getOrderPeriod() + "m");
        AlipayTradeAppPayRequest appPayRequest = new AlipayTradeAppPayRequest();
        appPayRequest.setBizModel(appPayModel);
        appPayRequest.setNotifyUrl(clientFactory.getSystemAliPayProperty(dto.getSourceSys()).getPayNotifyUrl());
        log.info("支付宝手机app支付 request={}", JSON.toJSONString(appPayRequest));
        AlipayTradeAppPayResponse appPayResponse = clientFactory.getAlipayClient(dto.getSourceSys()).sdkExecute(appPayRequest);
        log.info("支付宝手机app支付 response={}", JSON.toJSONString(appPayResponse));
        if (appPayResponse.isSuccess()) {
            return appPayResponse.getBody();
        }
        throw buildAliPayBizException(appPayResponse);
    }

    /**
     * 交易退款接口
     *
     * @param dto
     * @return
     * @throws AlipayApiException
     */
    private String tradeRefund(BankRefundOrderParamDTO dto) throws AlipayApiException {
        AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel();
        refundModel.setOutTradeNo(dto.getPayTrxNo());
        refundModel.setTradeNo(dto.getBankPayTrxNo());
        refundModel.setOutRequestNo(dto.getRefundTrxNo());
        refundModel.setRefundAmount(dto.getRefundAmount().toString());
        refundModel.setRefundReason(dto.getRefundReason());
        AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
        refundRequest.setBizModel(refundModel);
        refundRequest.setNotifyUrl(clientFactory.getSystemAliPayProperty(dto.getSourceSys()).getRefundNotifyUrl());
        log.info("支付宝交易退款 request={}", JSON.toJSONString(refundRequest));
        AlipayTradeRefundResponse refundResponse = clientFactory.getAlipayClient(dto.getSourceSys()).execute(refundRequest);
        log.info("支付宝交易退款 request={}", JSON.toJSONString(refundResponse));
        if (refundResponse.isSuccess()) {
            return refundResponse.getBody();
        }
        throw buildAliPayBizException(refundResponse);
    }


    /**
     * 单笔交易查询接口
     *
     * @param outTradeNo
     * @return
     * @throws AlipayApiException
     */
    private AlipayTradeQueryResponse queryAliPayTrade(String sourceSys, String outTradeNo) throws AlipayApiException {
        AlipayTradeQueryRequest tradeQueryRequest = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel tradeQueryModel = new AlipayTradeQueryModel();
        tradeQueryModel.setOutTradeNo(outTradeNo);
        tradeQueryRequest.setBizModel(tradeQueryModel);
        log.info("支付宝支付交易查询 request={}", JSON.toJSONString(tradeQueryRequest));
        AlipayTradeQueryResponse tradeQueryResponse = clientFactory.getAlipayClient(sourceSys).execute(tradeQueryRequest);
        log.info("支付宝支付交易查询 response={}", JSON.toJSON(tradeQueryResponse));
        // 查询二维码交易 未交易 refundResponse.isSuccess() 返回不为空 当未支付处理
        return tradeQueryResponse;
    }

    /**
     * 退款查询接口
     *
     * @param outTradeNo
     * @return
     * @throws AlipayApiException
     */
    private AlipayTradeFastpayRefundQueryResponse queryAliPayRefund(String sourceSys, String outTradeNo, String outRefundNo) throws AlipayApiException {
        AlipayTradeFastpayRefundQueryRequest refundQueryRequest = new AlipayTradeFastpayRefundQueryRequest();
        AlipayTradeFastpayRefundQueryModel refundQueryModel = new AlipayTradeFastpayRefundQueryModel();
        refundQueryModel.setOutTradeNo(outTradeNo);
        refundQueryModel.setOutRequestNo(outRefundNo);
        refundQueryRequest.setBizModel(refundQueryModel);
        log.info("支付宝退款查询 request={}", JSON.toJSONString(refundQueryRequest));
        AlipayTradeFastpayRefundQueryResponse refundQueryResponse = clientFactory.getAlipayClient(sourceSys).execute(refundQueryRequest);
        log.info("支付宝退款查询 response={}", JSON.toJSON(refundQueryResponse));
        if (refundQueryResponse.isSuccess()) {
            return refundQueryResponse;
        }
        throw buildAliPayBizException(refundQueryResponse);
    }

    /**
     * 查询对账单下载地址接口
     *
     * @return
     * @throws AlipayApiException
     */
    public String queryAliPayDataServiceBillDownloadUrl(String sourceSys, String date) throws AlipayApiException {
        AlipayDataDataserviceBillDownloadurlQueryRequest billQueryRequest = new AlipayDataDataserviceBillDownloadurlQueryRequest();
        AlipayDataDataserviceBillDownloadurlQueryModel billQueryModel = new AlipayDataDataserviceBillDownloadurlQueryModel();
        billQueryModel.setBillType("trade");
        billQueryModel.setBillDate(date);
        billQueryRequest.setBizModel(billQueryModel);
        log.info("支付宝对账单 request={}", JSON.toJSONString(billQueryRequest));
        AlipayDataDataserviceBillDownloadurlQueryResponse billQueryResponse = clientFactory.getAlipayClient(sourceSys).execute(billQueryRequest);
        log.info("支付宝对账单 response={}", JSON.toJSON(billQueryResponse));
        log.info("支付宝对账单 response={}", billQueryResponse.getBillDownloadUrl());
        log.info("支付宝对账单 response={}", billQueryResponse.isSuccess());
        if (billQueryResponse.isSuccess()) {
            return billQueryResponse.getBillDownloadUrl();
        }
        throw buildAliPayBizException(billQueryResponse);
    }

    /**
     * 包装支付宝业务异常
     *
     * @param resp
     * @return
     * @throws MSException
     */
    private MSException buildAliPayBizException(AlipayResponse resp) throws MSException {
        return new MSException(BizExceptEnum.ALIPAY_API_ERROR.getCode(), BizExceptEnum.ALIPAY_API_ERROR.getMsg() + ":" + resp.getSubMsg());
    }

    /**
     * 包装支付宝其他异常
     *
     * @param ex
     * @return
     */
    private MSException buildAliPayApiException(AlipayApiException ex) {
        return new MSException(BizExceptEnum.ALIPAY_API_ERROR.getCode(), BizExceptEnum.ALIPAY_API_ERROR.getMsg() + ":" + ex.getErrMsg());
    }

    public void downloadBillData(String sourceSys, LocalDate billDate) throws IOException {
        String downUrl = "";
        try {
            downUrl = queryAliPayDataServiceBillDownloadUrl(sourceSys, DateTimeFormatter.ofPattern("yyyyMMdd").format(billDate));
        } catch (AlipayApiException e) {
            log.error("获取{}系统{}的支付宝对账文件失败",sourceSys,billDate,e);
            return ;
        }
        Path tempDirectory = Files.createTempDirectory("alipay");
        Path path = Paths.get(tempDirectory.toString(), "bill" + System.currentTimeMillis() + ".zip");


    }
}